/*
author:杨文�?
email:yangwenchao@keynection.cn
*/
#pragma once
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"         // for queue

#include "esp_log.h"                // for GEMINAI_LOGE macro
#include "geminai_log.h"
#include "esp_timer.h"
#include "driver/uart.h"            // for uart_event_t

#if __has_include("driver/gptimer.h")
#include "driver/gptimer.h"
#else
#include "driver/timer.h"
#endif
#include "mbconfig.h"   // for options

#ifdef __cplusplus
extern "C" {
#endif

#if CONFIG_UART_ISR_IN_IRAM
#define MB_PORT_SERIAL_ISR_FLAG ESP_INTR_FLAG_IRAM
#else
#define MB_PORT_SERIAL_ISR_FLAG ESP_INTR_FLAG_LOWMED
#endif

#if CONFIG_FMB_TIMER_USE_ISR_DISPATCH_METHOD && MB_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
#define MB_PORT_ISR_ATTR IRAM_ATTR
#else
#define MB_PORT_ISR_ATTR
#endif

#define MB_PORT_TAG                     "GEMINAI_MODBUS"

#define MB_BAUD_RATE_DEFAULT            (115200)
#define MB_QUEUE_LENGTH                 (CONFIG_GMB_QUEUE_LENGTH)

#define MB_SERIAL_TASK_PRIO             (CONFIG_GMB_PORT_TASK_PRIO)
#define MB_SERIAL_TASK_STACK_SIZE       (CONFIG_GMB_PORT_TASK_STACK_SIZE)
#define MB_SERIAL_TOUT                  (3) // 3.5*8 = 28 ticks, TOUT=3 -> ~24..33 ticks

// Set buffer size for transmission
#define MB_SERIAL_BUF_SIZE              (CONFIG_GMB_SERIAL_BUF_SIZE)

// common definitions for serial port implementations
#define MB_SERIAL_TX_TOUT_MS            (50) // maximum time for transmission of longest allowed frame buffer
#define MB_SERIAL_TX_TOUT_TICKS         (pdMS_TO_TICKS(MB_SERIAL_TX_TOUT_MS)) // timeout for transmission
#define MB_SERIAL_RX_TOUT_MS            (500)
#define MB_SERIAL_RX_TOUT_TICKS         (pdMS_TO_TICKS(MB_SERIAL_RX_TOUT_MS)) // timeout for receive

#define MB_SERIAL_RESP_LEN_MIN          (4)


// The task affinity for Modbus stack tasks
#define MB_PORT_TASK_AFFINITY           (CONFIG_GMB_PORT_TASK_AFFINITY)

#define MB_TCP_READ_TIMEOUT_MS          (100) // read timeout in mS
#define MB_TCP_READ_TIMEOUT             (pdMS_TO_TICKS(MB_TCP_READ_TIMEOUT_MS))
#define MB_TCP_SEND_TIMEOUT_MS          (500) // send event timeout in mS
#define MB_TCP_SEND_TIMEOUT             (pdMS_TO_TICKS(MB_TCP_SEND_TIMEOUT_MS))
#define MB_TCP_PORT_MAX_CONN            (CONFIG_FMB_TCP_PORT_MAX_CONN)

// Set the API unlock time to maximum response time
// The actual release time will be dependent on the timer time
#define MB_MAX_RESPONSE_TIME_MS         (5000)



#define MB_PORT_HAS_CLOSE               (1) // Define to explicitly close port on destroy

// Define number of timer reloads per 1 mS
#define MB_TIMER_TICS_PER_MS            (20UL)
#define MB_TIMER_TICK_TIME_US           (1000 / MB_TIMER_TICS_PER_MS) // 50uS = one discreet for timer





#define MB_PORT_CHECK(a, ret_val, str, ...) \
    if (!(a)) { \
        GEMINAI_LOGE(MB_PORT_TAG, "%s(%u): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
        return ret_val; \
    }

#ifndef TRUE
#define TRUE            1
#endif

#ifndef FALSE
#define FALSE           0
#endif

typedef char    BOOL;

typedef unsigned char UCHAR;
typedef char    CHAR;

typedef unsigned short USHORT;
typedef short   SHORT;

typedef unsigned long ULONG;
typedef long    LONG;


typedef struct {
    esp_timer_handle_t xTimerIntHandle;
    USHORT usT35Ticks;
    BOOL xTimerState;
} xTimerContext_t;

void vMBPortEnterCritical(void);
void vMBPortExitCritical(void);

/*进入临界�?*/
#define ENTER_CRITICAL_SECTION( ) { ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port enter critical.", __func__); \
                                    vMBPortEnterCritical(); }

/*退出临界区*/
#define EXIT_CRITICAL_SECTION( )  { vMBPortExitCritical(); \
                                    ESP_EARLY_LOGD(MB_PORT_TAG,"%s: Port exit critical", __func__); }

#define MB_PORT_CHECK_EVENT( event, mask ) ( event & mask )
#define MB_PORT_CLEAR_EVENT( event, mask ) do { event &= ~mask; } while(0)

#define MB_PORT_PARITY_GET(parity) ((parity != UART_PARITY_DISABLE) ? \
                                        ((parity == UART_PARITY_ODD) ? MB_PAR_ODD : MB_PAR_EVEN) : MB_PAR_NONE)



BOOL xMBPortSerialWaitEvent(QueueHandle_t xMbUartQueue, uart_event_t* pxEvent, ULONG xTimeout);


/* ----------------------- Type definitions ---------------------------------*/

typedef enum
{
    EV_READY = 0x01,                   /* Startup finished. */
    EV_FRAME_RECEIVED = 0x02,          /* Frame received. */
    EV_EXECUTE = 0x04,                 /* Execute function. */
    EV_FRAME_SENT = 0x08,              /* Frame sent. */
    EV_FRAME_TRANSMIT = 0x10           /* Frame transmit. */
} eMBEventType;


typedef enum {
    EV_MASTER_NO_EVENT = 0x0000,
    EV_MASTER_TRANS_START = 0x0001,             /* Transaction start flag */
    EV_MASTER_READY = 0x0002,                   /* Startup finished. */
    EV_MASTER_FRAME_RECEIVED = 0x0004,          /* Frame received. */
    EV_MASTER_EXECUTE = 0x0008,                 /* Execute function. */
    EV_MASTER_FRAME_SENT = 0x0010,              /* Frame sent. */
    EV_MASTER_FRAME_TRANSMIT = 0x0020,          /* Frame transmission. */
    EV_MASTER_ERROR_PROCESS = 0x0040,           /* Frame error process. */
    EV_MASTER_PROCESS_SUCCESS = 0x0080,         /* Request process success. */
    EV_MASTER_ERROR_RESPOND_TIMEOUT = 0x0100,   /* Request respond timeout. */
    EV_MASTER_ERROR_RECEIVE_DATA = 0x0200,      /* Request receive data error. */
    EV_MASTER_ERROR_EXECUTE_FUNCTION = 0x0400   /* Request execute function error. */
} eMBMasterEventEnum;

typedef enum {
    EV_ERROR_INIT,             /* No error, initial state. */
    EV_ERROR_RESPOND_TIMEOUT,  /* Slave respond timeout. */
    EV_ERROR_RECEIVE_DATA,     /* Receive frame data error. */
    EV_ERROR_EXECUTE_FUNCTION, /* Execute function error. */
    EV_ERROR_OK                /* No error, processing completed. */
} eMBMasterErrorEventType;

typedef struct _MbEventType {
    eMBMasterEventEnum eEvent;      /* event itself. */
    uint64_t xTransactionId;        /* ID of the transaction */
    uint64_t xPostTimestamp;        /* timestamp of event posted */
    uint64_t xGetTimestamp;         /* timestamp of event get */ 
} xMBMasterEventType;



/*
Parity used for characters in serial mode.

The parity which should be applied to the characters sent over the serial
link. Please note that this values are actually passed to the porting
layer and therefore not all parity modes might be available.
*/
typedef enum
{
    MB_PAR_NONE,                /*!< No parity. */
    MB_PAR_ODD,                 /*!< Odd parity. */
    MB_PAR_EVEN                 /*!< Even parity. */
} eMBParity;

/* ----------------------- Supporting functions -----------------------------*/
BOOL            xMBPortEventInit( void );

BOOL            xMBPortEventPost( eMBEventType eEvent );

BOOL            xMBPortEventGet(  /*@out@ */ eMBEventType * eEvent );


BOOL            xMBMasterPortEventInit( void );

BOOL            xMBMasterPortEventPost( eMBMasterEventEnum eEvent );

BOOL            xMBMasterPortEventGet(  /*@out@ */ xMBMasterEventType * eEvent );

eMBMasterEventEnum
                xMBMasterPortFsmWaitConfirmation( eMBMasterEventEnum eEventMask, ULONG ulTimeout);

void            vMBMasterOsResInit( void );

BOOL            xMBMasterRunResTake( LONG time );

void            vMBMasterRunResRelease( void );

uint64_t        xMBMasterPortGetTransactionId( void );

/* ----------------------- Serial port functions ----------------------------*/

BOOL            xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
                                   UCHAR ucDataBits, eMBParity eParity );

void            vMBPortClose( void );

void            xMBPortSerialClose( void );

void            vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );

BOOL            xMBPortSerialGetByte( CHAR * pucByte );

BOOL            xMBPortSerialPutByte( CHAR ucByte );

BOOL            xMBPortSerialGetRequest( UCHAR **ppucMBSerialFrame, USHORT * pusSerialLength ) __attribute__ ((weak));

BOOL            xMBPortSerialSendResponse( UCHAR *pucMBSerialFrame, USHORT usSerialLength ) __attribute__ ((weak));


BOOL            xMBMasterPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
                                   UCHAR ucDataBits, eMBParity eParity );

void            vMBMasterPortClose( void );

void            xMBMasterPortSerialClose( void );

void            vMBMasterPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );

BOOL            xMBMasterPortSerialGetByte( CHAR * pucByte );

BOOL            xMBMasterPortSerialPutByte( CHAR ucByte );

BOOL            xMBMasterPortSerialGetResponse( UCHAR **ppucMBSerialFrame, USHORT * usSerialLength );

BOOL            xMBMasterPortSerialSendRequest( UCHAR *pucMBSerialFrame, USHORT usSerialLength );

void            vMBMasterRxFlush( void );



/* ----------------------- Timers functions ---------------------------------*/
BOOL            xMBPortTimersInit( USHORT usTimeOut50us );

void            xMBPortTimersClose( void );

void            vMBPortTimersEnable( void );

void            vMBPortTimersDisable( void );

void            vMBPortTimersDelay( USHORT usTimeOutMS );

BOOL            xMBMasterPortTimersInit( USHORT usTimeOut50us );

void            xMBMasterPortTimersClose( void );

void            vMBMasterPortTimersT35Enable( void );

void            vMBMasterPortTimersConvertDelayEnable( void );

void            vMBMasterPortTimersRespondTimeoutEnable( void );

void            vMBMasterPortTimersDisable( void );


/* ----------------- Callback for the master error process ------------------*/
void            vMBMasterErrorCBRespondTimeout( UCHAR ucDestAddress, const UCHAR* pucPDUData,
                                                USHORT ucPDULength );

void            vMBMasterErrorCBReceiveData( UCHAR ucDestAddress, const UCHAR* pucPDUData,
                                             USHORT ucPDULength );

void            vMBMasterErrorCBExecuteFunction( UCHAR ucDestAddress, const UCHAR* pucPDUData,
                                                 USHORT ucPDULength );

void            vMBMasterCBRequestSuccess( void );

/* ----------------------- Callback for the protocol stack ------------------*/
/*
Callback function for the porting layer when a new byte is
available.

Depending upon the mode this callback function is used by the RTU or
ASCII transmission layers. In any case a call to xMBPortSerialGetByte()
must immediately return a new character.

return <code>TRUE</code> if a event was posted to the queue because
a new byte was received. The port implementation should wake up the
tasks which are currently blocked on the eventqueue.
*/
extern          BOOL( *pxMBFrameCBByteReceived ) ( void );

extern          BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );

extern          BOOL( *pxMBPortCBTimerExpired ) ( void );


extern          BOOL( *pxMBMasterFrameCBByteReceived ) ( void );

extern          BOOL( *pxMBMasterFrameCBTransmitterEmpty ) ( void );

extern          BOOL( *pxMBMasterPortCBTimerExpired ) ( void );


#ifdef __cplusplus
}
#endif

